<?php
/*
 * description：
 * author：wh
 * email：
 * createTime：{2024/4/22} {15:15} 
 */

namespace wanghua\general_utility_tools_php\tool;


use think\Db;
use wanghua\general_utility_tools_php\http\Curl;

class Ip
{
    //重定向地址
    public $redirect_url = 'index/err/fail';

    /**
     * desc：是否在中国内(白嫖查询，不保证每次都能查询成功)
     * author：wh
     * @return mixed
     */
    public function ip_is_china($ip, $type='ipapi'){
        if($this->isPrivateIP($ip)){
            return true;//国内
        }
        switch ($type){
            case 'qifu_baidu':
                return $this->qihuBaidu($ip);
                break;
            case 'ipapi':
                return $this->ipApi($ip);
                break;
            default:
                return $this->ipApi($ip);
        }
    }


    /**
     * desc：是否为私有IP
     * author：wh
     * @param $ip
     * @return bool
     */
    function isPrivateIP($ip) {
        // 将IP地址字符串转换为整数
        $ipLong = ip2long($ip);
        // 检查转换是否成功以及IP地址是否属于私有地址段
        if ($ipLong !== false) {
            // A类私有地址
            if ($ipLong >= ip2long('10.0.0.0') && $ipLong <= ip2long('10.255.255.255')) {
                return true;
            }
            // B类私有地址
            elseif ($ipLong >= ip2long('172.16.0.0') && $ipLong <= ip2long('172.31.255.255')) {
                return true;
            }
            // C类私有地址
            elseif ($ipLong >= ip2long('192.168.0.0') && $ipLong <= ip2long('192.168.255.255')) {
                return true;
            }
        }
        // 如果不在私有地址范围内或转换失败，则不是私有IP
        return false;
    }

    /**
     * desc：百度ip查询
     *
     * author：wh
     * 查询结果：
     * {"code":"Success","data":{"continent":"亚洲","country":"中国","zipcode":"401120","timezone":"UTC+8",
     * "accuracy":"区县","owner":"中国移动","isp":"中国移动","source":"数据挖掘","areacode":"CN","adcode":"500112",
     * "asnumber":"9808","lat":"29.813215","lng":"106.743200","radius":"35.6842","prov":"重庆市","city":"重庆市",
     * "district":"渝北区"},"charge":true,"msg":"查询成功","ip":"183.227.88.66","coordsys":"WGS84"}
     * @param $ip
     * @return bool
     */
    function qihuBaidu($ip){
        if($this->isPrivateIP($ip)){
            return true;//国内
        }
        if(cache($ip)){
            Tools::log_to_write_txt(['读取缓存ip地址:'.$ip,'$res(yes为国内)'=>cache($ip)]);
            return cache($ip) == 'yes';
        }
        //先走缓存
        if($this->getRecord($ip)){
            return false;
        }



        $res = $this->curl_get($ip);
        Tools::log_to_write_txt(['error'=>'IP查询,curl结束：'.$ip,'$res'=>$res]);
        if($res['code'] == 200){//请求成功
            if(empty($res['data'])){
                Tools::log_to_write_txt(['error'=>'IP查询,curl结果为空','$res'=>$res]);
            }
            //校验ip属地
            $data = json_decode($res['data'],true);
            if($data['code'] != 'Success'){//查询失败
                Tools::log_to_write_txt(['error'=>'IP查询成功，但结果返回为失败.','$res'=>$res]);
                return true;
            }
            //查询成功
            if(empty($data['data']['country'])){
                //country为空时默认为国内
                Tools::log_to_write_txt(['error'=>'IP查询成功，但country返回为空.','$res'=>$res]);
                return true;
            }
            if($data['data']['country'] == '中国'){
                //缓存ip地址
                cache($ip,'yes');//国内
                return true;
            }
            $this->insertRecord($ip,$data['data']['continent'].' '.$data['data']['prov'],$data['data']['country'],'no');
            //缓存ip地址
            cache($ip,'no');//国外
            Tools::log_to_write_txt(['ip地址不在中国内，禁止访问:'.$ip,'curl data'=>$res['data']]);
            //return redirect($this->redirect_url);
            return false;
        }
        return true;
    }
    /**
     * ipapi查询IP
     *
     * 查询IP归属哪个国家（白嫖查询，不保证每次都能查询成功）
     *
     * CN表示中国，其它表示国外
     */
    function ipApi($ip){
        if($this->isPrivateIP($ip)){
            return true;//国内
        }
        if(cache($ip)){
            Tools::log_to_write_txt(['读取缓存ip地址:'.$ip,'$res(yes为国内)'=>cache($ip)]);
            return cache($ip) == 'yes';
        }
        //先走缓存
        if($this->getRecord($ip)){
            return false;
        }
        /**  cache($ip)为空时进行实时查询  */

        $url = "https://ipapi.co/{$ip}/country/";
        $curl = curl_init();
        curl_setopt_array($curl, array(
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 0,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => 'GET',
            CURLOPT_HTTPHEADER => array(
                'User-Agent: Apifox/1.0.0 (https://apifox.com)',
                'Accept: */*',
                'Host: ipapi.co',
                'Connection: keep-alive'
            ),
        ));

        $response = curl_exec($curl);

        curl_close($curl);
        if($response == 'CN'){
            cache($ip,'yes');//国内
            return true;
        }
        $this->insertRecord($ip,$response,$response);
        Tools::log_to_write_txt(['ip地址不在中国内，禁止访问:'.$ip,'$response'=>$response,'$url'=>$url]);
        cache($ip,'no');//国外
        return false;
    }

    /**
     * desc：
     * author：wh
     * @param string $territory 属地
     * @param string $country 国家
     * @param string $is_china 是否中国
     */
    private function insertRecord($ip,$territory='',$country='',$is_china='no'){
        /**

        CREATE TABLE `fa_ip_attack_record` (
        `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
        `ip` varchar(30) NOT NULL DEFAULT '' COMMENT 'IP',
        `territory` varchar(50) DEFAULT '' COMMENT '归属地',
        `country` varchar(15) DEFAULT '' COMMENT '国家',
        `is_china` varchar(10) NOT NULL DEFAULT '' COMMENT '是否中国',
        `url` text NOT NULL COMMENT '带域名查询参数',
        `params` text COMMENT '参数',
        `header` text COMMENT '请求头',
        `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '攻击时间',
        PRIMARY KEY (`id`) USING BTREE,
        KEY `index_ip` (`ip`) USING BTREE
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IP攻击记录';

         */
        //IP攻击记录
        Db::table('fa_ip_attack_record')
            ->insert([
                'ip'=>$ip,
                'territory'=>$territory,
                'country'=>$country,
                'is_china'=>$is_china,
                'url'=>request()->url(true),
                'params'=>json_encode(input(),JSON_UNESCAPED_UNICODE),
                'header'=>json_encode(request()->header(),JSON_UNESCAPED_UNICODE),
            ]);
    }

    /**
     * desc：查询IP攻击记录
     * author：wh
     * @param $ip
     */
    private function getRecord($ip){
        return Db::table('fa_ip_attack_record')
            ->where('ip',$ip)
            ->field('id,is_china')
            ->find();
    }

    /**
     * desc：curl
     * author：wh
     * @param $ip
     * @return array
     */
    function curl_get($ip){
        $curl = curl_init();

        curl_setopt_array($curl, array(
            CURLOPT_URL => 'https://qifu.baidu.com/ip/geo/v1/district?ip='.$ip,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 0,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION => 'CURL_HTTP_VERSION_1_1',
            CURLOPT_CUSTOMREQUEST => 'GET',
            CURLOPT_HTTPHEADER => array(
                'User-Agent: Apifox/1.0.0 (https://apifox.com)',
                'Accept: */*',
                'Host: qifu.baidu.com',
                'Connection: keep-alive'
            ),
        ));

        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        $data = curl_exec($curl);

        // 显示错误信息
        if (curl_error($curl)) {
            //print "Error: ".curl_errno($curl).'-' . curl_error($curl);
            //返回错误码
            return ['code' => curl_errno($curl), 'msg' => curl_error($curl)];
        } else {
            //关闭句柄
            curl_close($curl);
            // 返回的内容
            return ['code' => 200, 'msg' => 'cURL ok', 'data' => $data];
        }
    }
}